import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { BaseAlcConfig } from './base-alc.config';
import { ACLCanType, ACLType } from '@base/types';

/**
 * ACL 控制服务，[在线文档](https://ng-alain.com/acl)
 *
 * 务必在根目录注册 `DelonACLModule.forRoot()` 才能使用服务
 */

@Injectable({
    providedIn: 'root'
})
export class BaseAlcService {
    private roles: string[] = [];
    private abilities: Array<number | string> = [];
    private full = false;
    private aclChange = new BehaviorSubject<ACLType | boolean | null>(null);

    /** ACL变更通知 */
    get change(): Observable<ACLType | boolean | null> {
        return this.aclChange.asObservable();
    }

    /** 获取所有数据 */
    get data(): object {
        return {
            full: this.full,
            roles: this.roles,
            abilities: this.abilities,
        };
    }

    constructor(private options: BaseAlcConfig) { }

    private static parseACLType(val: string | string[] | number | number[] | ACLType | null): ACLType {
        let t: ACLType;
        if (typeof val === 'number') {
            t = { ability: [val] };
        } else if (Array.isArray(val) && val.length > 0 && typeof val[0] === 'number') {
            t = { ability: val };
        } else if (typeof val === 'object' && !Array.isArray(val)) {
            t = { ...val };
        } else if (Array.isArray(val)) {
            t = { role: val as string[] };
        } else {
            t = { role: val == null ? [] : [val] };
        }

        return { except: false, ...t };
    }

    /**
     * 设置当前用户角色或权限能力（会先清除所有）
     */
    set(value: ACLType): void {
        this.abilities = [];
        this.roles = [];
        this.add(value);
        this.aclChange.next(value);
    }

    /**
     * 标识当前用户为全量，即不受限
     */
    setFull(val: boolean): any {
        this.full = val;
        this.aclChange.next(val);
    }

    /**
     * 设置当前用户权限能力（会先清除所有）
     */
    setAbility(abilities: Array<number | string>): any{
        this.set({ ability: abilities } as ACLType);
    }

    /**
     * 设置当前用户角色（会先清除所有）
     */
    setRole(roles: string[]): any {
        this.set({ role: roles } as ACLType);
    }

    /**
     * 为当前用户增加角色或权限能力
     */
    add(value: ACLType): any {
        if (value.role && value.role.length > 0) {
            this.roles.push(...value.role);
        }
        if (value.ability && value.ability.length > 0) {
            this.abilities.push(...value.ability);
        }
    }

    /**
     * 为当前用户附加角色
     */
    attachRole(roles: string[]): any {
        for (const val of roles) {
            if (!this.roles.includes(val)) {
                this.roles.push(val);
            }
        }
        this.aclChange.next(this.data);
    }

    /**
     * 为当前用户附加权限
     */
    attachAbility(abilities: Array<number | string>): any {
        for (const val of abilities) {
            if (!this.abilities.includes(val)) {
                this.abilities.push(val);
            }
        }
        this.aclChange.next(this.data);
    }

    /**
     * 为当前用户移除角色
     */
    removeRole(roles: string[]): void {
        for (const val of roles) {
            const idx = this.roles.indexOf(val);
            if (idx !== -1) {
                this.roles.splice(idx, 1);
            }
        }
        this.aclChange.next(this.data);
    }

    /**
     * 为当前用户移除权限
     */
    removeAbility(abilities: Array<number | string>): void {
        for (const val of abilities) {
            const idx = this.abilities.indexOf(val);
            if (idx !== -1) {
                this.abilities.splice(idx, 1);
            }
        }
        this.aclChange.next(this.data);
    }

    /**
     * 当前用户是否有对应角色，其实 `number` 表示Ability
     *
     * - 当 `full: true` 或参数 `null` 时返回 `true`
     * - 若使用 `ACLType` 参数，可以指定 `mode` 校验模式
     */
    can(roleOrAbility: ACLCanType | null): boolean {
        const { preCan } = this.options;
        if (preCan) {
            roleOrAbility = preCan(roleOrAbility!);
        }

        const t = BaseAlcService.parseACLType(roleOrAbility);
        let result = false;
        if (this.full === true || !roleOrAbility) {
            result = true;
        } else {
            if (t.role && t.role.length > 0) {
                if (t.mode === 'allOf') {
                    result = t.role.every(v => this.roles.includes(v));
                } else {
                    result = t.role.some(v => this.roles.includes(v));
                }
            }
            if (t.ability && t.ability.length > 0) {
                if (t.mode === 'allOf') {
                    result = (t.ability as any[]).every(v => this.abilities.includes(v));
                } else {
                    result = (t.ability as any[]).some(v => this.abilities.includes(v));
                }
            }
        }

        return t.except === true ? !result : result;
    }

    /** @inner */
    parseAbility(value: ACLCanType): ACLCanType {
        if (typeof value === 'number' || typeof value === 'string' || Array.isArray(value)) {
            value = { ability: Array.isArray(value) ? value : [value] } as ACLType;
        }
        delete value.role;
        return value;
    }

    /**
     * 当前用户是否有对应权限点
     */
    canAbility(value: ACLCanType): boolean {
        return this.can(this.parseAbility(value));
    }
}